package com.push.mq.records;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Chronometer;
import android.widget.TextView;

import com.loyal.base.notify.NotifyNotification;
import com.loyal.kit.DeviceUtil;
import com.loyal.kit.OutUtil;
import com.loyal.kit.TimeUtil;
import com.push.mq.MQServerImpl;
import com.push.mq.R;
import com.push.mq.impl.ServerImpl;
import com.push.mq.utils.FileUtil;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.io.File;

public class RecordService extends Service implements MQServerImpl, SurfaceHolder.Callback, View.OnClickListener, MediaRecorder.OnInfoListener {
    private static final String CHANNEL_ID = "mqChannelID";
    private static final String CHANNEL_NAME = "mqChannelName";
    private WindowManager windowManager;
    private View parentView;
    private TextView textCylsh;
    private Chronometer mRecordTime;
    private Camera camera = null;
    private MediaRecorder mediaRecorder = null;
    private SurfaceHolder surfaceHolder;
    private View recordStopView;
    //MQ推送
    private MqttAndroidClient mqttAndroidClient;
    private MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();

    @Override
    public void onCreate() {
        try {
            initNotify();
            initViews();
            initMqTT();
        } catch (Exception e) {
            e.printStackTrace();
            //FileUtil.saveCrashInfo("oncreate.txt", e);
        }
    }

    void initNotify() {
        Notification.Builder notifyBuilder = new Notification.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("视频正在录制中...")
                .setSmallIcon(R.mipmap.ic_launcher);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notifyBuilder.setChannelId(CHANNEL_ID);
        }
        final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (null == nm)
            return;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            nm.createNotificationChannel(mChannel);
        }

        Notification notification = notifyBuilder.build();
        notification.defaults = Notification.DEFAULT_LIGHTS;
        notification.ledARGB = Color.GREEN;// 控制 LED 灯的颜色，一般有红绿蓝三种颜色可选
        //灯闪烁时需要设置下面两个变量
        notification.ledOnMS = 1000;// 指定 LED 灯亮起的时长，以毫秒为单位
        notification.ledOffMS = 1000;// 指定 LED 灯暗去的时长，也是以毫秒为单位
        notification.flags = Notification.FLAG_SHOW_LIGHTS;
        startForeground(1234, notification);
    }

    private void initMqTT() {
        if (null == mqttAndroidClient) {
            mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), mqServer(), clientId());
        }
        mqttAndroidClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                if (reconnect) {
                    OutUtil.println("Reconnected to : " + serverURI);
                    // Because Clean Session is true, we need to re-subscribe
                    subscribeToTopic();
                } else {
                    OutUtil.println("Connected to: " + serverURI);
                }
            }

            @Override
            public void connectionLost(Throwable cause) {
                reConnect();
                OutUtil.println("The Connection was lost.");
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                OutUtil.println("Callback: " + new String(message.getPayload()));
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
            }
        });
        mqttConnectOptions.setAutomaticReconnect(true);
        mqttConnectOptions.setCleanSession(true);
        // 设置超时时间，单位：秒
        mqttConnectOptions.setConnectionTimeout(10);
        // 心跳包发送间隔，单位：秒   心跳时间，单位为秒。即多长时间确认一次Client端是否在线
        mqttConnectOptions.setKeepAliveInterval(20);
        try {
            mqttAndroidClient.connect(mqttConnectOptions, null, actionListener);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    private void initViews() {
        windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
        parentView = LayoutInflater.from(this).inflate(R.layout.layout_record, null);
        SurfaceView surfaceView = parentView.findViewById(R.id.surfaceView);
        textCylsh = parentView.findViewById(R.id.text_cylsh);
        textCylsh.setText("1907231524");
        mRecordTime = parentView.findViewById(R.id.record_time);
        recordStopView = parentView.findViewById(R.id.recordStop);
        recordStopView.setOnClickListener(this);
        int windowType;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0新特性
            windowType = (WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
        } else {
            windowType = (WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        }

        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                windowType,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT
        );

        //layoutParams.gravity = Gravity.START | Gravity.TOP;
        windowManager.addView(parentView, layoutParams);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceView.setKeepScreenOn(true);
    }

    private IMqttActionListener actionListener = new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            try {
                DisconnectedBufferOptions disconnectedBufferOptions = new DisconnectedBufferOptions();
                disconnectedBufferOptions.setBufferEnabled(true);
                disconnectedBufferOptions.setBufferSize(100);
                disconnectedBufferOptions.setPersistBuffer(false);
                disconnectedBufferOptions.setDeleteOldestMessages(true);
                mqttAndroidClient.setBufferOpts(disconnectedBufferOptions);
                subscribeToTopic();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
            OutUtil.println("Failed to connect to: " + mqServer());
            reConnect();
        }
    };

    private void reConnect() {
        if (null == mqttAndroidClient) {
            return;
        }
        if (!mqttAndroidClient.isConnected()) {
            try {
                mqttAndroidClient.connect(mqttConnectOptions, null, actionListener);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(holder);
    }

    private void startPreview(SurfaceHolder holder) {
        if (null == camera) {
            camera = Camera.open();
        }
        try {
            camera.setPreviewDisplay(holder);
            Camera.Parameters params = camera.getParameters();
            //设置相机的横竖屏(竖屏需要旋转90°)
            if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
                params.set("orientation", "portrait");
                camera.setDisplayOrientation(90);
            } else {
                params.set("orientation", "landscape");
                camera.setDisplayOrientation(0);
            }
            //设置聚焦模式
            params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            //缩短Recording启动时间
            params.setRecordingHint(true);
            //影像稳定能力
            if (params.isVideoStabilizationSupported())
                params.setVideoStabilization(true);
            camera.setParameters(params);
            camera.startPreview();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void startRecord() {
        recordStopView.setTag("startRecord");
        try {
            if (null != mediaRecorder) {
                onInfo(mediaRecorder, MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
            }
            if (mediaRecorder == null) {
                mediaRecorder = new MediaRecorder();
            }
            camera.unlock();
            mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
            mediaRecorder.setCamera(camera);
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
            mediaRecorder.setMaxDuration(30 * 60 * 1000);
            mediaRecorder.setOnInfoListener(this);
            mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

            String videoName = "1907231234";//ObjectUtil.replaceNull(messageBean.getCylsh());
            videoName = TextUtils.isEmpty(videoName) ? TimeUtil.getDateTime("yyyyMMddHHmmss") : videoName;
            final String fileName = videoName;
            Handler handler = new Handler(getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textCylsh.setText(fileName);
                }
            });
            File current = new File(FileUtil.datePath);
            if (!current.exists())
                FileUtil.mkDirs(current);
            mediaRecorder.setOutputFile(String.format("%s/%s.mp4", FileUtil.datePath, videoName));
            mediaRecorder.prepare();
            //开始录制
            mediaRecorder.start();
            mRecordTime.setBase(SystemClock.elapsedRealtime());
            mRecordTime.start();
        } catch (Exception e) {
            e.printStackTrace();
            mRecordTime.stop();
        }
    }

    private void stopRecord() {
        OutUtil.println("stopRecord");
        recordStopView.setTag("");
        if (null != mediaRecorder) {
            playStopSound();
        }
        releaseMediaRecorder();
    }

    private void releaseMediaRecorder() {
        try {
            mRecordTime.setBase(SystemClock.elapsedRealtime());
            mRecordTime.stop();
            if (null != mediaRecorder) {
                try {
                    mediaRecorder.stop();
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                    mediaRecorder = null;
                    mediaRecorder = new MediaRecorder();
                }
                mediaRecorder.reset();
                mediaRecorder.release();
                mediaRecorder = null;
            }
            if (camera != null) {
                camera.lock();
            }
        } catch (Exception e) {
            e.printStackTrace();
            FileUtil.saveCrashInfo("stopRecord.txt", e);
        }
    }

    private void releaseCamera() {
        try {
            if (null != camera) {
                camera.setPreviewCallback(null);
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            FileUtil.saveCrashInfo("releaseCamera.txt", e);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        releaseCamera();
    }

    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
            stopRecord();
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.recordStop) {
            String tag = (String) v.getTag();
            if (TextUtils.isEmpty(tag)) {
                exit();
                return;
            }
            onInfo(mediaRecorder, MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void subscribeToTopic() {
        try {
            mqttAndroidClient.subscribe(mqTopic(), 0, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    OutUtil.println("Subscribed!");
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    OutUtil.println("Failed to subscribe");
                }
            });
            // THIS DOES NOT WORK!
            mqttAndroidClient.subscribe(mqTopic(), 0, new IMqttMessageListener() {
                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    String result = new String(message.getPayload());
                    if (TextUtils.equals("1", result)) {
                        startRecord();
                    } else if (TextUtils.equals("0", result)) {
                        onInfo(mediaRecorder, MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
                    }
                    //NotifyNotification.cancel(RecordService.this);
                    //NotifyNotification.notify(RecordService.this, result);
                    // message Arrived!
                    //OutUtil.println("SUBSCRIBE->NOTIFY: " + topic + " : " + result);
                }
            });
        } catch (MqttException e) {
            OutUtil.println("Exception whilst subscribing");
            e.printStackTrace();
        }
    }

    @Override
    public String mqServer() {
        return ServerImpl.serverUri;
    }

    @Override
    public String clientId() {
        return DeviceUtil.deviceSerial();
    }

    @Override
    public String mqTopic() {
        return "recordTopic";
    }

    private void breakConnect() {
        if (null != mqttAndroidClient) {
            mqttAndroidClient.close();
            mqttAndroidClient = null;
        }
    }

    private void playStopSound() {
        try {
            final MediaPlayer mMediaPlayer = MediaPlayer.create(this, R.raw.stop);
            mMediaPlayer.start();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (mMediaPlayer.isPlaying())
                        mMediaPlayer.stop();
                    mMediaPlayer.release();
                }
            }, mMediaPlayer.getDuration());
        } catch (Exception e) {
            e.printStackTrace();
            //FileUtil.saveCrashInfo("releasePlayer.txt", e);
        }
    }

    private void exit() {
        breakConnect();
        Intent intent = new Intent(this, RecordService.class);
        stopService(intent);
    }

    // Stop recording and remove SurfaceView
    @Override
    public void onDestroy() {
        stopForeground(true);
        breakConnect();
        stopRecord();
        if (null != camera) camera.lock();
        releaseCamera();
        if (null != windowManager)
            windowManager.removeView(parentView);
    }
}
